{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9158eaa",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| default_exp wh_transcribe"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3494cca7",
   "metadata": {},
   "source": [
    "## Precompute Whisper transcriptions for VQ bottleneck distilation\n",
    "\n",
    "Doing transcription means sampling from the Whisper auto-regresive decoder. This is too slow to do for each training batch. Fortunately the trainscriptions are small text snippets so we can precompute them once for the whole dataset.\n",
    "\n",
    "We use segments from Voice Activity Detection to reduce any boundary issues, the we use webdataset to yields multiple chunks from a FLAC file we only load once. The VAD segments are merged into longer chunks to make Whisper processing more efficent (it always processes 30s at a time)\n",
    "\n",
    "**Usage:**  \n",
    "```\n",
    "python -m whisperspeech.wh_transcribe librilight-large-wo6454-flac-000002.tar\n",
    "```\n",
    "\n",
    "You can pass in either a URL or a local file name. Either way it will expect a `vad` file in the local directory. The result will go into a file in the current directory named after the source file but replacing `flac` with `txt`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cf56fcb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The autoreload extension is already loaded. To reload it, use:\n",
      "  %reload_ext autoreload\n"
     ]
    }
   ],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ac6d8f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "import os\n",
    "import io\n",
    "import time\n",
    "import torch\n",
    "import torchaudio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ecbdddfd",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "from pathlib import Path\n",
    "import json\n",
    "from fastprogress import progress_bar, master_bar\n",
    "import numpy as np\n",
    "import random\n",
    "\n",
    "import whisper\n",
    "\n",
    "from torch import nn\n",
    "import torch.nn.functional as F\n",
    "from torch.utils.data.dataloader import DataLoader\n",
    "\n",
    "from fastcore.script import *\n",
    "\n",
    "from whisperspeech import vad\n",
    "import webdataset as wds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9bad8563",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pylab as plt\n",
    "import IPython"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bbd549c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "flac_url = 'https://huggingface.co/datasets/collabora/librilight-webdataset/resolve/main/librilight-small-flac-000000.tar'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0aa81fea",
   "metadata": {},
   "outputs": [],
   "source": [
    "flac_url = './librilight-small-flac-000000.tar'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8026a16",
   "metadata": {},
   "source": [
    "## Merge VAD segments into longer chunks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc38db57",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| exporti\n",
    "# let's make it a bit more conservative\n",
    "# with full 30 second chunks it sometimes misses a small part of the transcript\n",
    "def random_cutter(dur):\n",
    "    if random.random() < 0.5:\n",
    "        return dur > 28 * (random.random()*0.95+0.05)\n",
    "    else:\n",
    "        return dur > 28\n",
    "\n",
    "def chunk_merger(segments, should_cut=lambda x: x > 28):\n",
    "    if len(segments) == 0: return segments\n",
    "    curr_start = segments[0][0]\n",
    "    curr_end = 0\n",
    "    merged = []\n",
    "\n",
    "    for ts,te in segments:\n",
    "        if should_cut(te - curr_start) and curr_end - curr_start > 0:\n",
    "            merged.append((curr_start, curr_end))\n",
    "            curr_start = ts\n",
    "        curr_end = te\n",
    "    merged.append((curr_start, curr_end))\n",
    "    return merged"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "524375d4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<style>\n",
       "    /* Turns off some styling */\n",
       "    progress {\n",
       "        /* gets rid of default border in Firefox and Opera. */\n",
       "        border: none;\n",
       "        /* Needs to be in here for Safari polyfill so background images work as expected. */\n",
       "        background-size: auto;\n",
       "    }\n",
       "    progress:not([value]), progress:not([value])::-webkit-progress-bar {\n",
       "        background: repeating-linear-gradient(45deg, #7e7e7e, #7e7e7e 10px, #5c5c5c 10px, #5c5c5c 20px);\n",
       "    }\n",
       "    .progress-bar-interrupted, .progress-bar-interrupted::-webkit-progress-bar {\n",
       "        background: #F44336;\n",
       "    }\n",
       "</style>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      <progress value='335' class='' max='335' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      100.00% [335/335 00:00&lt;00:00]\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# load some VAD ouputs\n",
    "ds = wds.WebDataset(\n",
    "    vad.flac_to_vad_name(flac_url)\n",
    ").decode().to_tuple('vad.npy')\n",
    "chunks = [x[0] for x in progress_bar(ds, total='noinfer')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57abcc3b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(46, 28)"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# quick test\n",
    "len(chunks[0]), len(chunk_merger(chunks[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4e3edf8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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zdLT002FChVMjRo1skxLTU01JUqUMC1atLBrnzdvnnF2djZnzpwxkydPNj4+PnbvvwzBwcGmbdu22a738uXLpkSJEqZs2bImPT091xqXL19u2rRpYwIDA42rq6upWLGiGT9+fJb9aOaxaEze9q0Z1q9fb9uv+/r6mscff9wcOnTIrk/G63vw4EHz5JNPGj8/P1OrVi27aRmye20zxmVG32PHjpmoqCjj6+trfHx8TM+ePc2VK1fs1pmxj8x4/7m7u5uHH37Y7Nu3zxhjzOzZs02lSpWMm5ubadKkSZbPq1sphLHs/wQHBys2NlYHDhy45QlsEyZM0CuvvKIuXbqoT58+OnfunGbMmKHGjRvrhx9+sH0rmTVrlgYOHKhGjRpp6NChOnHihNq1a6fixYtne0g2JiZGHh4eGjVqlI4fP64ZM2aoaNGicnZ21sWLFzV27Fht375dc+bMUUhIiMaMGZPvmiTp4sWLatWqlTp06KAuXbro888/10svvaQaNWqodevWqlatmsaPH68xY8aoX79+atSokSTpkUceydc2nTdvnqKiohQZGanJkyfr6tWrmjVrlho2bKgffvjB7sS3tLQ0RUZGKiwsTP/+97/17bff6o033lClSpVs317S09P12GOPaefOnerfv7+qVq2qFStWKCoqym69zz33nE6fPq1169Zp3rx52da2YMECXb58Wc8995ycnJw0ZcoUdejQQf/9739VtGjRHJ9TfHy8mjVrphs3bmjUqFHy9PTU+++/Lw8Pj1tujw8++ECDBw9Wp06d9MILLyg5OVn79u3Tjh071L17d3Xo0EE//fSTPvvsM02bNk2lSpWSJJUuXdq2jA0bNmjx4sUaOHCgSpUqdcuTB7t06aIKFSooJiZG27dv1/Tp03Xx4kV98sknt6z3Znmp7WYJCQl65JFHdPXqVQ0ePFglS5bU3Llz9fjjj+vzzz9X+/bt7fpPmjRJzs7OevHFF5WYmKgpU6aoR48etzwf4fnnn9fnn3+ugQMHKjQ0VOfPn9d3332nw4cPq3bt2nr55ZeVmJioX3/9VdOmTZMkeXl52S3jtddek6urq1588UWlpKTI1dVVhw4d0vLly9W5c2eFhIQoISFB7733npo0aaJDhw4pKCgox5pWr16trl27qkaNGoqJidHFixfVu3dvlS1bNtv+txqLeRnPmY0dO1bjxo1TRESE+vfvr6NHj2rWrFnatWuXtm7dmusYz+x29gcHDx5Uo0aN5OPjo5EjR6po0aJ677331LRpU23evFlhYWHq0KGD/Pz8NHToUD355JNq06ZNltcmPzXcar8m/bkPefzxx/Xdd9+pX79+qlatmvbv369p06bpp59+yvXnEicnJ3Xv3l0TJ07UwYMH9cADD9imrVmzRhcuXFCPHj3s5pk/f76aNWumgIAAdevWTaNGjdKXX36pzp07577Rb+Ll5aX27dvro48+0qFDh+zWm9mcOXPk5eWlYcOGycvLSxs2bNCYMWOUlJSkqVOn5jhfXvetkvTtt9+qdevWqlixosaOHatr165pxowZatCggfbs2ZNln9S5c2dVrlxZEydO1J85JKt58+apT58+ql+/vvr16ydJqlSpkl2fLl26KCQkRDExMdqzZ48+/PBDlSlTRpMnT7br95///EcrV65UdHS0pD8/Vx999FGNHDlS7777rgYMGKCLFy9qypQp6tWrV64/IWaRr0h0l1m7dq1xcXExLi4uJjw83IwcOdJ88803JjU11a7fiRMnjIuLi5kwYYJd+/79+02RIkVs7SkpKaZkyZKmXr165vr167Z+c+bMMZKyPZJTvXp1u/U9+eSTxsnJybRu3dpuXeHh4XYpPK81GfN/32w/+eQTW1tKSooJCAgwHTt2tLXt2rUrT9/WMmQ+knP58mXj5+dn+vbta9cvPj7e+Pr62rVnfFMdP368Xd+HHnrI1KlTx/b/L774wkgyb731lq0tLS3NNG/ePEut0dHR2X7bzfj2XLJkSXPhwgVb+4oVK4wk8+WXX+b6PIcMGWIkmR07dtjazp49a3x9fW95JOeJJ54wDzzwQK7Lnzp1arZHxIz581uKs7OzOXjwYLbTsjuS8/jjj9v1GzBggJFkfvzxR2NM3o/k3Kq2zEcEMrbTf/7zH1vb5cuXTUhIiKlQoYJJS0szxvzf2K9WrZpJSUmx9X377beNJLN///4s67qZr6+viY6OzrVP27Ztsz1CkLHuihUrmqtXr9pNS05OttWYIS4uzri5udmN0+y2X40aNUy5cuXM5cuXbW2bNm3KcqQiP2Mxp/GcnbNnzxpXV1fTsmVLu+cwc+ZMI8n8z//8j60tL0dyjMn//qBdu3bG1dXV/Pzzz7a206dPG29vb9O4cWNb281Hs24ltxryul/LOLJy87g05s9v+JLM1q1bc63h4MGDRpIZPXq0XXu3bt2Mu7u7SUxMtLUlJCSYIkWKmA8++MDW9sgjj5gnnngiy3JzO5JjjDHTpk0zksyKFStyrS/zODbGmOeee84UK1bM7khV5iM5+dm31qpVy5QpU8acP3/e1vbjjz8aZ2dn88wzz9jaMvZBTz75ZJaaMh/JMcYYT0/PbMdiRt9evXrZtbdv396ULFnSrk2ScXNzs9tHvffee0aSCQgIMElJSbb20aNH57g/y0mhPvH4n//8p2JjY/X444/rxx9/1JQpUxQZGamyZctq5cqVtn5Lly5Venq6unTpot9//932CAgIUOXKlbVx40ZJ0vfff6/z58+rb9++dr899ujRQ8WLF8+2hmeeecbuG1ZYWJiMMerVq5ddv7CwMJ06dUo3btzIV00ZvLy87H77dXV1Vf369fXf//73NrdeVuvWrdOlS5f05JNP2tXk4uKisLCwLDVJf34rv1mjRo3salqzZo2KFi2qvn372tqcnZ1tiT0/unbtavc6ZHwzvNU2+Oqrr/Twww+rfv36trbSpUtn+QaXHT8/P/3666/atWtXvuvN0KRJE4WGhua5f+ZtM2jQIEl/Po+/01dffaX69evbXY3i5eWlfv366cSJEzp06JBd/2effdbuHKa8vh5+fn7asWOHTp8+fdu1RkVFZTkS5+bmZjsvJy0tTefPn5eXl5eqVKmiPXv25Lis06dPa//+/XrmmWfsjko0adJENWrUyHae2x2LOfn222+VmpqqIUOG2J1b1LdvX/n4+Gj16tW3tdy8SktL09q1a9WuXTtVrFjR1h4YGKju3bvru+++U1JSUoGvNy/7tSVLlqhatWqqWrWq3X6pefPmkpTtfulmoaGheuihh7Rw4UJb25UrV7Ry5Uo9+uij8vHxsbUvXLhQzs7O6tixo63tySef1Ndff62LFy/m+7lJ0uXLl3Ptd/M4vnz5sn7//Xc1atRIV69e1ZEjR3KcL6/71jNnzmjv3r3q2bOnSpQoYWt/8MEH9c9//jPb/Urm/frtyu7z4fz581nGUosWLeyOJoWFhUmSOnbsKG9v7yzt+XmfFeqQI0n16tXT0qVLdfHiRe3cuVOjR4/W5cuX1alTJ9tO+dixYzLGqHLlyipdurTd4/Dhw7aTlDPOwL///vvt1lGkSJEcf2K477777P7v6+srSSpfvnyW9vT0dCUmJuarpgzlypXLco+C4sWL5/uNl5tjx45Jkpo3b56lprVr12apyd3dPctPH5lr+uWXXxQYGKhixYrZ9cu8jfMi87bO+JC51Tb45ZdfVLly5SztVapUueU6X3rpJXl5eal+/fqqXLmyoqOjtXXr1nxULYWEhOSrf+ZaK1WqJGdn57/9fka//PJLttukWrVqtuk3u93XY8qUKTpw4IDKly+v+vXra+zYsfkOB9lt0/T0dNvJnm5ubipVqpRKly6tffv22d532cnpfZ9Tm3T7z/1WNWTe/q6urqpYsWKOVwcVlHPnzunq1as5vv7p6ek6depUga83L/u1Y8eO6eDBg1n2Sf/4xz8k5e0ikx49eiguLk7btm2TJC1fvlxXr17N8kXn008/Vf369XX+/HkdP35cx48f10MPPaTU1FQtWbIkX8/tjz/+kCS7D+nsHDx4UO3bt5evr698fHxUunRpW/C71bjNy741p7El/fna/v7777py5Ypde373WTnJ6/skP5+j2c2fm0J9Ts7NXF1dVa9ePdWrV0//+Mc/9Oyzz2rJkiV69dVXlZ6eLicnJ3399ddycXHJMm9OvynnRXbLy63d/O/vm/mt6VbLKwgZ94OYN2+eAgICskzPfGZ9TjX9Xe7ENsisWrVqOnr0qFatWqU1a9boiy++0LvvvqsxY8bk+fLgvJz7k5vMHwI53ZArLS3tL60nv2739ejSpYsaNWqkZcuWae3atZo6daomT56spUuX2s7DuJXstunEiRP1yiuvqFevXnrttddUokQJOTs7a8iQITne6+R2OWIsZsjt9b/T78m/Ki/bMT09XTVq1NCbb76Zbd/MH4TZefLJJzVy5EgtWLBAjzzyiBYsWKDixYurTZs2tj7Hjh2zHbHN7kvR/Pnzbeee5MWBAwck5f6F7tKlS2rSpIl8fHw0fvx4VapUSe7u7tqzZ49eeumlAh+3efVX91kZ8vo+ud3P0bywTMi5WcaNqc6cOSPpz2/CxhiFhITY0n92goODJUnHjx9Xs2bNbO03btzQiRMn9OCDDxZYjXmtKT/yejfK3GqSpDJlyuR4yWR+BQcHa+PGjbp69ardN47jx49n6ftX68+thoyjVDc7evRonub39PRU165d1bVrV6WmpqpDhw6aMGGCRo8eLXd39wKv+9ixY3bfpI4fP6709HTb0cSMb0OZ732T3bf9/NQWHByc7TbJOGSe8f4oCIGBgRowYIAGDBigs2fPqnbt2powYYIt5NzONv3888/VrFkzffTRR3btly5dsp10nZ2b3/eZZdeWV/nd9tKfY/Lmn4tSU1MVFxdn934sXrx4tvc9+uWXX+zmzc/6S5curWLFiuX4+js7O+cpTGRWEO+NSpUq6ccff1SLFi1ue3lBQUFq1qyZlixZoldeeUXr1q1Tz5497X5unT9/vooWLap58+Zl+XD97rvvNH36dJ08eTLLUYfs/PHHH1q2bJnKly9vOxKanU2bNun8+fNaunSpGjdubGuPi4u75Tryum+9eWxlduTIEZUqVUqenp63XF92/q59dkEq1D9Xbdy4MdtEl/EbY8bhuQ4dOsjFxUXjxo3L0t8Yo/Pnz0v6MxyVLFlSH3zwge3cGenPwV+QPwvlp6b8yBiot3t328jISPn4+GjixIm6fv16lukZ98fI7zKvX79ud9O59PR0vfPOO1n6/tX6c9KmTRtt375dO3futLWdO3dO8+fPv+W8mV8HV1dXhYaGyhhj20YFXXfmbZNx9+6MAODj46NSpUppy5Ytdv3efffdLMvKT21t2rTRzp07FRsba2u7cuWK3n//fVWoUCFf5xXlJC0tLcsh+DJlyigoKEgpKSl2ded2qD47Li4uWd5LS5Ys0W+//ZbrfEFBQapevbo++eQT208MkrR582bt378/XzXcLD/bPiIiQq6urpo+fbrdc/joo4+UmJiotm3b2toqVaqk7du3KzU11da2atWqLD8n5Wf9Li4uatmypVasWGH3s2hCQoIWLFighg0b2p27klcF8d7o0qWLfvvtt2xvXHnt2rUsP7XkpEePHjp79qyee+45Xb9+Pdurqho1aqSuXbuqU6dOdo8RI0ZIkj777LNbrifjRpMXLlzQyy+/nGsQyAhTN7/mqamp2b6XM8vrvjUwMFC1atXS3Llz7V6HAwcOaO3atXZHs/LL09Pzrr+beqE+kjNo0CBdvXpV7du3V9WqVZWamqpt27Zp0aJFqlChgp599llJf+4UXn/9dY0ePdp2Sbi3t7fi4uK0bNky9evXTy+++KJcXV01duxYDRo0SM2bN1eXLl104sQJzZkzR5UqVSrQ1JrXmvK7TD8/P82ePVve3t7y9PRUWFhYnn9f9fHx0axZs/T000+rdu3a6tatm0qXLq2TJ09q9erVatCggWbOnJmvmtq1a6f69etr+PDhOn78uKpWraqVK1fqwoULkuy/CdSpU0eSNHjwYEVGRsrFxcXuJl23a+TIkZo3b55atWqlF154wXYJeXBwsPbt25frvC1btlRAQIAaNGggf39/HT58WDNnzlTbtm1tv7Vn1P3yyy+rW7duKlq0qB577LHb/nYUFxenxx9/XK1atVJsbKw+/fRTde/eXTVr1rT16dOnjyZNmqQ+ffqobt262rJli3766acsy8pPbaNGjdJnn32m1q1ba/DgwSpRooTmzp2ruLg4ffHFF3m+2V5uLl++rHLlyqlTp06qWbOmvLy89O2332rXrl1644037OpetGiRhg0bpnr16snLy0uPPfZYrst+9NFHNX78eD377LN65JFHtH//fs2fP9/u6EZOJk6cqCeeeEINGjTQs88+q4sXL2rmzJmqXr26XfDJj/yM59KlS2v06NEaN26cWrVqpccff1xHjx7Vu+++q3r16tmdnNunTx99/vnnatWqlbp06aKff/5Zn376aZbLd/O7P3j99de1bt06NWzYUAMGDFCRIkX03nvvKSUlRVOmTLmtbfBX90mS9PTTT2vx4sV6/vnntXHjRjVo0EBpaWk6cuSIFi9erG+++SbHPytxs44dO2rAgAFasWKFypcvb3fkZMeOHTp+/LgGDhyY7bxly5ZV7dq1NX/+fL300ku29t9++02ffvqppD+P3hw6dEhLlixRfHy8hg8frueeey7Xmh555BEVL15cUVFRGjx4sJycnDRv3rw8/RyTn33r1KlT1bp1a4WHh6t37962S8h9fX3/0p8eqVOnjr799lu9+eabCgoKUkhIiO3k4LtGnq/Dugt9/fXXplevXqZq1arGy8vLuLq6mvvvv98MGjTIJCQkZOn/xRdfmIYNGxpPT0/j6elpqlataqKjo83Ro0ft+k2fPt0EBwcbNzc3U79+fbN161ZTp04d06pVK1ufjEtZlyxZYjdvxmXZu3btsmvPuKTu3Llz+a4p46ZZmWV3c6gVK1bYbjqn27wZ4MaNG01kZKTx9fU17u7uplKlSqZnz57m+++/t1u3p6dnlmVmd5nhuXPnTPfu3W03rOrZs6fZunWrkWQWLlxo63fjxg0zaNAgU7p0aePk5GRbTm6XrCrTJdM52bdvn2nS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eXh4yNfXV71799awYcNUokQJ+fj4aNCgQQoPD9fDDz8sSWrZsqVCQ0P19NNPa8qUKYqPj9e//vUvRUdH247EPP/885o5c6ZGjhypXr16acOGDVq8eLFWr+aqJgAAkDf5OpIza9YsJSYmqmnTpgoMDLQ9Fi1aZOszbdo0Pfroo+rYsaMaN26sgIAALV261DbdxcVFq1atkouLi8LDw/XUU0/pmWee0fjx4219QkJCtHr1aq1bt041a9bUG2+8oQ8//FCRkZEF8JQBAMC94C/dJ+duxn1ygLzjPjkA7hYF+fl9WyceA7CWwhjcCWYAboU/0AkAACyJkAMAACyJkAMAACyJkAMAACyJE48BFEqcLA3gVjiSAwAALImQAwAALImQAwAALImQAwAALImQAwAALImQAwAALImQAwAALImQAwAALImQAwAALImQAwAALIk/6wAAdwh/igK4sziSAwAALImQAwAALImQAwAALImQAwAALImQAwAALImQAwAALIlLyAEAOeKydxRmHMkBAACWRMgBAACWRMgBAACWRMgBAACWRMgBAACWxNVVAABL4YowZOBIDgAAsCRCDgAAsCRCDgAAsCRCDgAAsCRCDgAAsCSurgIAwMG4IuzvwZEcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSYQcAABgSXd1yHnnnXdUoUIFubu7KywsTDt37nR0SQAAoJC4a0POokWLNGzYML366qvas2ePatasqcjISJ09e9bRpQEAgELgrg05b775pvr27atnn31WoaGhmj17tooVK6b/+Z//cXRpAACgECji6AKyk5qaqt27d2v06NG2NmdnZ0VERCg2NjbbeVJSUpSSkmL7f2JioiQpKSmpwOtLT7la4MsEAKAw+Ts+X29erjHmLy/rrgw5v//+u9LS0uTv72/X7u/vryNHjmQ7T0xMjMaNG5elvXz58n9LjQAA3Mt83/p7l3/58mX5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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist([te-ts for x in chunks for ts,te in x])\n",
    "plt.title('Segment length distribution straight out of the VAD algorithm');"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ebf12ba7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjAAAAGzCAYAAAAxPS2EAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABC3UlEQVR4nO3deVhWdf7/8Reg3C5437ixCeI2o+JWUeE9llo6oqFp6ihpimU6FlZqmfmbRq1mwnRabNNxmskWrbSyRVLDDcdEKwq3klGjrBQwTW5XVPj8/ujifL0FFQy7Ofp8XNd9XZxzPuec9/nc5755cTb8jDFGAAAANuLv6wIAAAAqigADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwADAABshwBzCfPz89PYsWN9su5p06bJz89PP/30U4XnnTdvnvz8/PTtt99WfmGV6Ntvv5Wfn5/+8Y9/VOpyy9r+rl27qmvXrpW6nrPx8/PTtGnTrOFf815eiCZNmmjEiBG/ybou1I4dO9SjRw+5XC75+fnpvffe83VJVZ5dPtcXy5mfK/x6BBgb2rVrl/785z+rWbNmqlGjhpxOpzp16qRZs2bp2LFjvi7vkvPRRx/Z8otn/fr1mjZtmg4ePOjrUkqpyrWVR1JSkrZs2aK///3veu2113T11VdrwYIFeuaZZ3xdGnDZqObrAlAxqamp+tOf/iSHw6Hhw4erbdu2OnHihNatW6eJEydq27Ztmjt3rq/LvKR89NFHeuGFF3waYj7++OMKz7N+/Xo98sgjGjFihIKDg8s937Fjx1St2sX9ajhXbdnZ2fL3r7p/Wx07dkwZGRn6y1/+4nWEc8GCBdq6davGjRvnu+JQZf0Wn6vLDb1pIzk5OUpMTFR0dLRWrVql8PBwa1pycrJ27typ1NRUH1aIiyUwMPCiLr+4uFgnTpxQjRo1VKNGjYu6rvNxOBw+Xf/57Nu3T5IqFAov1OnvS2U7cuSIateuXenLvRwcP35cgYGBFQravv5cXYqq7p85KGXGjBk6fPiw/v3vf3uFlxItWrTQfffdV2r8e++9p7Zt28rhcKhNmzZatmyZ1/QRI0aoSZMmpeYrufbhdCXX1ZxvmWX57rvv1KJFC7Vt21Z5eXnnbX+mpUuX6vrrr1ft2rVVp04dJSQkaNu2baW2JSgoSD/++KP69eunoKAgNWzYUA888ICKioq82u7fv1/Dhg2T0+lUcHCwkpKStGnTJvn5+WnevHnW8l544QVr20teZ5o7d66aN28uh8Oha665Rp999lm5tmnbtm268cYbVbNmTUVGRupvf/ubiouLS7Ur6xqY5557Tm3atFGtWrVUt25d6zSG9Mt7N3HiRElS06ZNrbpLrj8oeR/nz5+vNm3ayOFwWO/h2c7V//TTTxo0aJCcTqfq16+v++67T8ePH7eml1wTVNJ3pzt9meerraxrYL755hv96U9/Ur169VSrVi117NixVFhfs2aN/Pz8tHDhQv39739XZGSkatSooW7dumnnzp2lajrTd999p7vvvlstW7ZUzZo1Vb9+ff3pT3/yumZj2rRpio6OliRNnDhRfn5+atKkibp27arU1FR999131vac/pkqLCzU1KlT1aJFCzkcDkVFRenBBx9UYWFhqX462/tSluLiYk2bNk0RERGqVauWbrjhBn311Vel+rDk+pP09HTdfffdCgkJUWRkpDW9PJ8tSdq+fbsGDhyoevXqqUaNGrr66qv1wQcflGpXnv06KSlJDRo00MmTJ0vN36NHD7Vs2fKs2y398plo27atNm/erC5duqhWrVpq0aKF3n77bUlSenq64uLiVLNmTbVs2VIrVqwotYwff/xRd9xxh0JDQ63vsv/85z9ebUr2qzfffFMPP/ywGjVqpFq1asnj8UiSFi1apJiYGNWoUUNt27bV4sWLy/xOPdu1ZTt37rSORLpcLt1+++06evSo17zHjh3TvffeqwYNGqhOnTq6+eab9eOPP17219VwBMZGPvzwQzVr1kx/+MMfyj3PunXr9O677+ruu+9WnTp19Oyzz2rAgAHavXu36tevf0F1XMgyd+3apRtvvFH16tVTWlqaGjRoUKF1vvbaa0pKSlJ8fLyeeOIJHT16VLNnz9Z1112nL7/80uvLoqioSPHx8YqLi9M//vEPrVixQk8++aSaN2+uu+66S9IvX/x9+vTRp59+qrvuukutWrXS+++/r6SkJK/1/vnPf9aePXuUlpam1157rczaFixYoEOHDunPf/6z/Pz8NGPGDPXv31/ffPONqlevftZtys3N1Q033KBTp07poYceUu3atTV37lzVrFnzvP3xr3/9S/fee68GDhxoBYnNmzdr48aNGjJkiPr376///e9/euONN/T0009b/d2wYUNrGatWrdLChQs1duxYNWjQoMwQe7pBgwapSZMmSklJ0YYNG/Tss8/q559/1quvvnreek9XntpOl5eXpz/84Q86evSo7r33XtWvX1+vvPKKbr75Zr399tu65ZZbvNpPnz5d/v7+euCBB1RQUKAZM2Zo6NCh2rhx4znr+uyzz7R+/XolJiYqMjJS3377rWbPnq2uXbvqq6++Uq1atdS/f38FBwdr/PjxuvXWW3XTTTcpKChItWvXVkFBgX744Qc9/fTTkqSgoCBJv+xrN998s9atW6fRo0erdevW2rJli55++mn973//K3UBcEXel8mTJ2vGjBnq06eP4uPjtWnTJsXHx3sFy9PdfffdatiwoaZMmaIjR45IKv9na9u2berUqZMaNWpk7a8LFy5Uv3799M4771jvQ3n362HDhunVV1/V8uXL1bt3b2t8bm6uVq1apalTp57z/ZKkn3/+Wb1791ZiYqL+9Kc/afbs2UpMTNT8+fM1btw4jRkzRkOGDNHMmTM1cOBAff/996pTp46kX/arjh07WqGxYcOGWrp0qUaOHCmPx1PqVOBjjz2mwMBAPfDAAyosLFRgYKBSU1M1ePBgtWvXTikpKfr55581cuRINWrU6Ly1lxg0aJCaNm2qlJQUffHFF3rppZcUEhKiJ554wmozYsQILVy4UMOGDVPHjh2Vnp6uhISEcq/jkmVgCwUFBUaS6du3b7nnkWQCAwPNzp07rXGbNm0yksxzzz1njUtKSjLR0dGl5p86dao5cxcp7zJL5t23b5/5+uuvTUREhLnmmmvMgQMHzlv3yy+/bCSZnJwcY4wxhw4dMsHBwWbUqFFe7XJzc43L5fIan5SUZCSZRx991KvtlVdeaWJjY63hd955x0gyzzzzjDWuqKjI3HjjjUaSefnll63xycnJpfrBGGNycnKMJFO/fn2v7Xr//feNJPPhhx+eczvHjRtnJJmNGzda4/Lz843L5fLafmOM6dKli+nSpYs13LdvX9OmTZtzLn/mzJmlllNCkvH39zfbtm0rc9rUqVOt4ZL38uabb/Zqd/fddxtJZtOmTcaY/+uP0/vubMs8V23R0dEmKSnJGi7pp//+97/WuEOHDpmmTZuaJk2amKKiImOMMatXrzaSTOvWrU1hYaHVdtasWUaS2bJlS6l1ne7o0aOlxmVkZBhJ5tVXX7XGlWznzJkzvdomJCSU+Tl67bXXjL+/v1f9xhgzZ84cI8l88skn1rhzvS9nys3NNdWqVTP9+vXzGj9t2jQjyasPSz5T1113nTl16pQ1viKfrW7dupl27dqZ48ePW+OKi4vNH/7wB/O73/3OGlfe/bqoqMhERkaawYMHe637qaeeMn5+fuabb7455/Z36dLFSDILFiywxm3fvt3qww0bNljjly9fXmrfHDlypAkPDzc//fST13ITExONy+Wy9oeS/apZs2al9pF27dqZyMhIc+jQIWvcmjVrjKRS+8LZPld33HGHV7tbbrnF1K9f3xrOzMw0ksy4ceO82o0YMaLUMi83nEKyiZLDlSV/PZRX9+7d1bx5c2u4ffv2cjqd+uabby64loosc+vWrerSpYuaNGmiFStWqG7duhVeX1pamg4ePKhbb71VP/30k/UKCAhQXFycVq9eXWqeMWPGeA1ff/31XvUtW7ZM1atX16hRo6xx/v7+Sk5OrnB9gwcP9tqu66+/XpLO28cfffSROnbsqGuvvdYa17BhQw0dOvS86wwODtYPP/xQ7lNVZenSpYtiYmLK3f7Mvrnnnnsk/bIdF9NHH32ka6+9Vtddd501LigoSKNHj9a3336rr776yqv97bff7nXNUHnfj9OPEJw8eVL79+9XixYtFBwcrC+++OKC61+0aJFat26tVq1aee2/N954oySV2n/L+76sXLlSp06d0t133+01vuR9KcuoUaMUEBBgDZf3s3XgwAGtWrVKgwYN0qFDh6x2+/fvV3x8vHbs2KEff/xRUvn3a39/fw0dOlQffPCBDh06ZI2fP3++/vCHP6hp06bn7YOgoCAlJiZawy1btlRwcLBat26tuLg4a3zJzyX7gDFG77zzjvr06SNjjNe2x8fHq6CgoNR7npSU5LWP7NmzR1u2bNHw4cOto23SL+9fu3btzlt7ibK+q/bv329955ecQqzI+3y54BSSTTidTkny+qCXR+PGjUuNq1u3rn7++ecLrqUiy+zTp49CQ0O1fPlyrw95RezYsUOSrC/8M5X0TYkaNWqUOh1xZn3fffedwsPDVatWLa92LVq0qHB9Z/ZHSZg5Xx9/9913Xl+yJc537l+SJk2apBUrVujaa69VixYt1KNHDw0ZMkSdOnUqd93l+QVxut/97ndew82bN5e/v/9Ff67H2fqpdevW1vS2bdta4y/0/Th27JhSUlL08ssv68cff5QxxppWUFBwwfXv2LFDX3/99VlPkeXn53sNl/d9+e677ySV3mfr1at31j8Uzlx2eT9bO3fulDFGf/3rX/XXv/61zLb5+flq1KhRhfbr4cOH64knntDixYs1fPhwZWdnKzMzU3PmzClzHWeKjIwsdU2ay+VSVFRUqXHS/+0D+/bt08GDBzV37tyz3rV5vvflbP1fMq68ofdc+6vT6dR3330nf3//Uuu/kO+qSw0BxiacTqciIiK0devWCs13+l9bpzv9y7msi1IllbrotSLLLDFgwAC98sormj9/vv785z+fr9wylVz899prryksLKzU9DNvTTxbfRdLRfqjsrRu3VrZ2dlasmSJli1bpnfeeUcvvviipkyZokceeaRcyyjPtTbnUtYF3mU52350sVzo+3HPPffo5Zdf1rhx4+R2u62H1CUmJpZ5YXV5FRcXq127dnrqqafKnH7mL9tf+76cy5nLLu9nq6TdAw88oPj4+DKXfSG/UGNiYhQbG6vXX39dw4cP1+uvv67AwEANGjSoXPOf7b0+3z5Qsj233XZbqeveSrRv395r+GK9L774/rhUEGBspHfv3po7d64yMjLkdrsrbbl169Yt84FiJX9h/BozZ85UtWrVrAt+hwwZUuFllJyuCgkJUffu3X91TZIUHR2t1atX6+jRo15HYcq6W+Vsv5gro4aSv4BPl52dXa75a9eurcGDB2vw4ME6ceKE+vfvr7///e+aPHmyatSoUel179ixw+uvwJ07d6q4uNi6yLPkL8cz96Wy9qOK1BYdHV1mn2zfvt2aXhnefvttJSUl6cknn7TGHT9+vNwP2zvbNjVv3lybNm1St27dKvU9KdnunTt3er0v+/fvL/cR1vJ+tpo1ayZJql69+nk/gxXdr4cPH64JEyZo7969WrBggRISEi7oVHNFNGzYUHXq1FFRUdEFf6ec3v9nKs9dbxVZT3FxsXJycryOglbmOuyKa2Bs5MEHH1Tt2rV15513lnkb8q5duzRr1qwKL7d58+YqKCjQ5s2brXF79+7V4sWLf1W90i9f6nPnztXAgQOVlJRU5i2X5xMfHy+n06nHH3+8zFsuS57LUdFlnjx5Uv/617+sccXFxdYt06creVZGZT819qabbtKGDRv06aefWuP27dun+fPnn3fe/fv3ew0HBgYqJiZGxhirjyq77jP75rnnnpMk9erVS9IvRwkbNGigtWvXerV78cUXSy2rIrXddNNN+vTTT5WRkWGNO3LkiObOnasmTZpU6DqecwkICCj1V+9zzz1X7iNIJXcinWnQoEH68ccfvfa1EseOHbPuBqqobt26qVq1apo9e7bX+Oeff77cyyjvZyskJERdu3bVP//5T+3du/es7aSK79e33nqr/Pz8dN999+mbb77RbbfdVu76L1RAQIAGDBigd955p8yj2uX5TomIiFDbtm316quv6vDhw9b49PR0bdmypdJqLTnidebnqOTzdznjCIyNNG/eXAsWLNDgwYPVunVrryfxrl+/XosWLbqg/yGTmJioSZMm6ZZbbtG9995r3Ub5+9///lddvFjC399fr7/+uvr166dBgwbpo48+Ous597I4nU7Nnj1bw4YN01VXXaXExEQ1bNhQu3fvVmpqqjp16lShL21J6tevn6699lrdf//92rlzp1q1aqUPPvhABw4ckOT913RsbKwk6d5771V8fLwCAgK8Lhy8UA8++KBee+019ezZU/fdd591u2l0dLRXmCxLjx49FBYWpk6dOik0NFRff/21nn/+eSUkJFgXepfU/Ze//EWJiYmqXr26+vTpc8EPL8vJydHNN9+snj17KiMjQ6+//rqGDBmiDh06WG3uvPNOTZ8+XXfeeaeuvvpqrV27Vv/73/9KLasitT300EN644031KtXL917772qV6+eXnnlFeXk5Oidd96ptKf29u7dW6+99ppcLpdiYmKUkZGhFStWlPtxA7GxsXrrrbc0YcIEXXPNNQoKClKfPn00bNgwLVy4UGPGjNHq1avVqVMnFRUVafv27Vq4cKGWL1+uq6++usL1hoaG6r777tOTTz5pvS+bNm3S0qVL1aBBg3Id7anIZ+uFF17Qddddp3bt2mnUqFFq1qyZ8vLylJGRoR9++EGbNm2SVPH9umHDhurZs6cWLVqk4ODg3+z24OnTp2v16tWKi4vTqFGjFBMTowMHDuiLL77QihUrrO+Cc3n88cfVt29fderUSbfffrt+/vlnPf/882rbtq1XqPk1YmNjNWDAAD3zzDPav3+/dRt1yefqYh0htgXf3PyEX+N///ufGTVqlGnSpIkJDAw0derUMZ06dTLPPfec1y2OkkxycnKp+c+8TdUYYz7++GPTtm1bExgYaFq2bGlef/31s95GXZ5lnn4bdYmjR4+aLl26mKCgIK9bHM905m3UJVavXm3i4+ONy+UyNWrUMM2bNzcjRowwn3/+udUmKSnJ1K5du9Qyy9qWffv2mSFDhpg6deoYl8tlRowYYT755BMjybz55ptWu1OnTpl77rnHNGzY0Pj5+VnLOdvttCX9VJ7bGzdv3my6dOliatSoYRo1amQee+wx8+9///u8t1H/85//NJ07dzb169c3DofDNG/e3EycONEUFBR4Lf+xxx4zjRo1Mv7+/l7LPNv7WFbtJX331VdfmYEDB5o6deqYunXrmrFjx5pjx455zXv06FEzcuRI43K5TJ06dcygQYNMfn5+mf1xttrK2j937dplBg4caIKDg02NGjXMtddea5YsWeLVpuR210WLFnmNP9ft3af7+eefze23324aNGhggoKCTHx8vNm+fXupes72vh8+fNgMGTLEBAcHl7qN9sSJE+aJJ54wbdq0MQ6Hw9StW9fExsaaRx55xOs9O9f7UpZTp06Zv/71ryYsLMzUrFnT3Hjjjebrr7829evXN2PGjLHalXymPvvsszKXU57PljG/vA/Dhw83YWFhpnr16qZRo0amd+/e5u233/ZqV979usTChQuNJDN69Ohyb3uXLl3KfJRAdHS0SUhIKDW+rL7Ny8szycnJJioqylSvXt2EhYWZbt26mblz53r1TVn7VYk333zTtGrVyjgcDtO2bVvzwQcfmAEDBphWrVqVWn9Zn6vTvyONKfv778iRIyY5OdnUq1fPBAUFmX79+pns7GwjyUyfPv2sfXSp8zOGK4WAEu+9955uueUWrVu3rkJ39ABVxcGDB1W3bl397W9/01/+8hdfl1Mu77//vvr166e1a9dat73b2RVXXKGGDRsqLS3toq0jKytLV155pV5//fVyPXrhUsQ1MLhsnfmfu4uKivTcc8/J6XTqqquu8lFVQPmV9d/nS/4j9pn/eqIq+9e//qVmzZp5PevHDk6ePKlTp055jVuzZo02bdpUqf1/tvfZ399fnTt3rrT12A3XwOCydc899+jYsWNyu90qLCzUu+++q/Xr1+vxxx+/qLeyApXlrbfe0rx586x/abBu3Tq98cYb6tGjhy2OIL755pvavHmzUlNTNWvWLNtdz/Hjjz+qe/fuuu222xQREaHt27drzpw5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VXh4uPV66623JEmBgYFasWKFevTooVatWun+++/XgAED9OGHH1rLCAgI0JIlSxQQECC3263bbrtNw4cP16OPPmq1adq0qVJTU5WWlqYOHTroySef1EsvvcQt1AAAQJLkZ4wxvi7iYvB4PHK5XCooKOB6GAC4jDR5KNXXJVTYt9MTfF1ClVHe398XfBcSAODSZ8cwgMsD/8wRAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYDgEGAADYToUCTEpKiq655hrVqVNHISEh6tevn7Kzs73aHD9+XMnJyapfv76CgoI0YMAA5eXlebXZvXu3EhISVKtWLYWEhGjixIk6deqUV5s1a9boqquuksPhUIsWLTRv3rwL20IAAHDJqVCASU9PV3JysjZs2KC0tDSdPHlSPXr00JEjR6w248eP14cffqhFixYpPT1de/bsUf/+/a3pRUVFSkhI0IkTJ7R+/Xq98sormjdvnqZMmWK1ycnJUUJCgm644QZlZWVp3LhxuvPOO7V8+fJK2GQAAGB3fsYYc6Ez79u3TyEhIUpPT1fnzp1VUFCghg0basGCBRo4cKAkafv27WrdurUyMjLUsWNHLV26VL1799aePXsUGhoqSZozZ44mTZqkffv2KTAwUJMmTVJqaqq2bt1qrSsxMVEHDx7UsmXLyqylsLBQhYWF1rDH41FUVJQKCgrkdDovdBMB4LLW5KFUX5dwWfh2eoKvS6gyPB6PXC7XeX9//6prYAoKCiRJ9erVkyRlZmbq5MmT6t69u9WmVatWaty4sTIyMiRJGRkZateunRVeJCk+Pl4ej0fbtm2z2py+jJI2JcsoS0pKilwul/WKior6NZsGAACqsAsOMMXFxRo3bpw6deqktm3bSpJyc3MVGBio4OBgr7ahoaHKzc212pweXkqml0w7VxuPx6Njx46V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wcFebS/n/aqsfpKkIUOGKDo6WhEREdq8ebMmTZqk7Oxsvfvuuz6s1je2bNkit9ut48ePKygoSIsXL1ZMTIyysrKq1P5EgEGl69Wrl/Vz+/btFRcXp+joaC1cuFAjR470YWW4VCQmJlo/t2vXTu3bt1fz5s21Zs0adevWzYeV+U5ycrK2bt3K9WbncbZ+Gj16tPVzu3btFB4erm7dumnXrl1q3rz5b12mT7Vs2VJZWVkqKCjQ22+/raSkJKWnp/u6rFI4hVRODRo0UEBAQKmrrfPy8hQWFuajquwhODhYv//977Vz505fl1JllexD7F8XplmzZmrQoMFlu4+NHTtWS5Ys0erVqxUZGWmNDwsL04kTJ3Tw4EGv9pfrfnW2fipLXFycJF2W+1RgYKBatGih2NhYpaSkqEOHDpo1a1aV258IMOUUGBio2NhYrVy50hpXXFyslStXyu12+7Cyqu/w4cPatWuXwsPDfV1KldW0aVOFhYV57V8ej0cbN25k/yqHH374Qfv377/s9jFjjMaOHavFixdr1apVatq0qdf02NhYVa9e3Wu/ys7O1u7duy+r/ep8/VSWrKwsSbrs9qmyFBcXq7CwsOrtT7/5ZcM29uabbxqHw2HmzZtnvvrqKzN69GgTHBxscnNzfV1alXL//febNWvWmJycHPPJJ5+Y7t27mwYNGpj8/Hxfl+ZThw4dMl9++aX58ssvjSTz1FNPmS+//NJ89913xhhjpk+fboKDg837779vNm/ebPr27WuaNm1qjh075uPKf3vn6qtDhw6ZBx54wGRkZJicnByzYsUKc9VVV5nf/e535vjx474u/Td11113GZfLZdasWWP27t1rvY4ePWq1GTNmjGncuLFZtWqV+fzzz43b7TZut9uHVf/2ztdPO3fuNI8++qj5/PPPTU5Ojnn//fdNs2bNTOfOnX1c+W/voYceMunp6SYnJ8ds3rzZPPTQQ8bPz898/PHHxpiqtT8RYCroueeeM40bNzaBgYHm2muvNRs2bPB1SVXO4MGDTXh4uAkMDDSNGjUygwcPNjt37vR1WT63evVqI6nUKykpyRjzy63Uf/3rX01oaKhxOBymW7duJjs727dF+8i5+uro0aOmR48epmHDhqZ69eomOjrajBo16rL8Q6KsPpJkXn75ZavNsWPHzN13323q1q1ratWqZW655Razd+9e3xXtA+frp927d5vOnTubevXqGYfDYVq0aGEmTpxoCgoKfFu4D9xxxx0mOjraBAYGmoYNG5pu3bpZ4cWYqrU/+RljzG93vAcAAODX4xoYAABgOwQYAABgOwQYAABgOwQYAABgOwQYAABgOwQYAABgOwQYAABgOwQYAABgOwQYAABgOwQYAABgOwQYAABgO/8fEhsnkDeEGWoAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist([te-ts for x in chunks for ts,te in chunk_merger(x)]);\n",
    "plt.title('Chunk length distribution after greedy merging');"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b92d200",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.03671825647504738"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(np.array([te-ts for x in chunks for ts,te in chunk_merger(x)]) < 10).mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4412834b",
   "metadata": {},
   "source": [
    "In the above distribution only 3,7% of the samples have < 10 seconds. We noticed that this limits the ability of the T2S model to generate short sequences reliably.\n",
    "\n",
    "It does not seem to matter for quantizing Whisper so we can keep this distribution (it uses less compute for training).\n",
    "\n",
    "For T2S we can add some more shorter chunks at random:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c3001e08",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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8cYbdebMGY0fP15Vq1bVrFmzivXp+V//+pdGjRqlfv36OUFi8+bNWr9+vfr3768+ffrou+++0xtvvKHnnnvO6e9atWo521i5cqUWLFigESNGqGbNmkWG2LPdeuutioiIUHJystatW6dp06bpl19+KfLN7EKKU9vZMjMz9ac//UnHjx/XqFGjVKNGDb322mvq1auX3n77bd18881u7Z944gl5e3vrgQceUFZWlqZOnaoBAwZo/fr1F6zriy++0Oeff66EhATVqVNHP/zwg2bOnKmOHTvqm2++UZUqVdSnTx8FBQXpvvvu0+23365u3brJ399fVatWVVZWlvbu3avnnntOkuTv7y/p17HWq1cvffbZZxo6dKiaNGmiLVu26LnnntN3331XaAJwSV+XtLQ03X777Ro2bJiGDBmiRo0aSZJmzpypZs2aqVevXqpYsaI++OAD3XPPPcrPz1dSUpLbNnbu3Kl+/fpp8ODBSkxM1KuvvqpBgwYpKipKzZo1k1Sy8Tp58mRNmTJFsbGxGj58uNLS0jRz5kx98cUXWrt2rdvvxS+//KIePXooISFBt9xyi2bOnKmEhATNmzdPo0eP1t13363+/fvrqaeeUr9+/fTjjz8qICCgyL7o06ePGjZs6LZs48aNev755xUcHOwsGzZsmObMmaM777xTo0aNUnp6ul588UVt2rTJrb6JEyfq8ccfV7du3dStWzd99dVXiouLU25u7gVfk7PNmzdPubm5GjlypA4fPqypU6fq1ltvVadOnbRq1SqNGzdOO3fu1PTp0/XAAw/o1VdfdR5bkve9M2fOKD4+Xm3bttXTTz+tKlWqSPo13CxYsEADBw5U69attXr1anXv3r3Y9RdnbPz000+68cYb5eXlpQkTJqhq1ar697//Xe6PphZiUCaysrKMJHPTTTcV+zGSjI+Pj9m5c6ez7OuvvzaSzPTp051liYmJJjw8vNDjJ02aZM59SYu7zYLHHjx40Hz77bcmLCzM3HDDDebw4cMXrXv27NlGkklPTzfGGHP06FETFBRkhgwZ4tYuIyPDuFwut+WJiYlGknn00Ufd2l577bUmKirKuf/f//7XSDLPP/+8sywvL8906tTJSDKzZ892liclJRXqB2OMSU9PN5JMjRo13PbrvffeM5LMBx98cMH9HD16tJFk1q9f7yw7cOCAcblcbvtvjDEd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+mt17ty5zMbSuT744AOdOnVK77//vtsn6KK+diiu4o7XgtcjLS1NDRo0cJbn5uYqPT291H6Hz+e7775TYmKievfurYceeqjQ+sjISC1fvlxt2rS5YFgs2I8dO3a47cfBgwd/0xHs4iqN973w8HDl5+crPT3d7UhScc7KK+nzFLXN0n6essYcmDL04IMPqmrVqrrrrruKPA15165deuGFF0q83cjISGVlZWnz5s3Osv379+vdd9/9TfVKv76pz5o1S/369VNiYqLef//9Em8jPj5egYGB+sc//qHTp08XWl9wXY6SbvP06dP617/+5SzLz893Tpk+W8F356V91dhu3bpp3bp12rBhg7Ps4MGD5/1keLZDhw653ffx8VHTpk1ljHH6qLTrPrdvpk+fLknq2rWrpF+PEtasWVNr1qxxa/fSSy8V2lZJauvWrZs2bNiglJQUZ9mxY8c0a9YsRURElGi+yIVUqFCh0KfK6dOnF/sIUsGZSOe69dZb9dNPP7mNtQInTpzQsWPHLq3gCyj41Hzu12CzZ8++5G0Wd7zGxsbKx8dH06ZNc3v+V155RVlZWSU6A6akcnJydPPNN+uqq67Sa6+9VmRgvPXWW5WXl6fHHnus0LozZ844YzI2NlaVKlXS9OnT3fbjcl1tuTTe9wrmRp37O1jwu1ta4uPjlZKSotTUVGfZ4cOHi/VeVp5wBKYMRUZGav78+brtttvUpEkTtyvxfv7551q4cOEl/X+OhIQEjRs3TjfffLNGjRrlnKp39dVX/6bJiwW8vb31+uuvq3fv3rr11lv14Ycfnvd73aIEBgZq5syZGjhwoK677jolJCSoVq1a2rNnj5YsWaI2bdroxRdfLFFNvXv3VqtWrXT//fdr586daty4sd5//33nOgxnv/FFRUVJkkaNGqX4+HhVqFDBbdLhpXrwwQc1d+5cdenSRffee69zWmp4eLhbmCxKXFycQkND1aZNG4WEhOjbb7/Viy++qO7duzsTvQvqfvjhh5WQkKBKlSqpZ8+eJbq41dnS09PVq1cvdenSRSkpKXr99dfVv39/tWzZ0mlz11136YknntBdd92l66+/XmvWrNF3331XaFslqW38+PF644031LVrV40aNUrVq1fXa6+9pvT0dP33v/8ttcPmPXr00Ny5c+VyudS0aVOlpKRo+fLlxb7cQFRUlN566y2N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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist([te-ts for x in chunks for ts,te in chunk_merger(x, random_cutter)])\n",
    "plt.title('Chunk length distribution after randomized merging');"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e398febe",
   "metadata": {},
   "source": [
    "## Merge the FLAC and VAD datasets\n",
    "\n",
    "First we want to merge the VAD dataset with the FLAC audio data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6abc83cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| exporti\n",
    "def merge_in(*datasets):\n",
    "    \"\"\"Merge multiple datasets into the current one returning samples with the union of keys.\n",
    "    \n",
    "    It requires (and validates) all datasets to have the same ordering of keys so you have\n",
    "    to use it before any sample shuffling. Shard shuffling is ok.\n",
    "    \"\"\"\n",
    "    def merge_loop(main_samples):\n",
    "        for samples in zip(*[main_samples]+[iter(x) for x in datasets]):\n",
    "            key = samples[0]['__key__']\n",
    "            news = {}\n",
    "            for s in samples:\n",
    "                assert s['__key__'] == key\n",
    "                news.update(s)\n",
    "            yield news\n",
    "    return merge_loop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1c3b7cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| exporti\n",
    "import copy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "442b6f1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| exporti\n",
    "# a workaround for https://github.com/webdataset/webdataset/issues/297\n",
    "# should be possible to use ds.compose here\n",
    "def wds_compose(ds, *args):\n",
    "    ds = copy.copy(ds)\n",
    "    ds.pipeline = copy.copy(ds.pipeline)\n",
    "    for f in args:\n",
    "        ds.append(f)\n",
    "    return ds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5ba9b8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "ds = wds_compose(vad.load_dataset(flac_url),\n",
    "    merge_in(wds.WebDataset(vad.flac_to_vad_name(flac_url)).decode())\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df6897e1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'__key__': 'small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb',\n",
       " '__url__': 'librilight-small-vad-000000.tar.gz',\n",
       " 'flac': (tensor([[0., 0., 0.,  ..., 0., 0., 0.]]), 16000),\n",
       " 'json': {'speaker': '100',\n",
       "  'book_meta': {'id': '2315',\n",
       "   'title': 'Sea Fairies',\n",
       "   'description': \"<p>In 1910, Baum hoped to end the Oz series and follow with a new series about a little girl named Trot and her sailor companion, Cap'n Bill. The Sea Fairies (1911) was the first book in the projected series and took Trot and Cap'n Bill under the sea where they had adventures with mermaids and other fantastic creatures. It was followed by Sky Island (1912) and then Baum returned to the Oz titles. He brought Trot and Cap'n Bill to Oz in the Scarecrow of Oz (1915). (Summary by Judy Bieber)</p>\",\n",
       "   'url_text_source': 'http://www.gutenberg.org/etext/4358',\n",
       "   'language': 'English',\n",
       "   'copyright_year': '1911',\n",
       "   'num_sections': '22',\n",
       "   'url_rss': 'https://librivox.org/rss/2315',\n",
       "   'url_zip_file': 'http://www.archive.org/download/sea_fairies_0812_librivox/sea_fairies_0812_librivox_64kb_mp3.zip',\n",
       "   'url_project': 'http://en.wikipedia.org/wiki/The_Sea_Fairies',\n",
       "   'url_librivox': 'https://librivox.org/the-sea-fairies-by-l-frank-baum/',\n",
       "   'url_other': None,\n",
       "   'totaltimesecs': 15311,\n",
       "   'authors': [{'id': '406',\n",
       "     'first_name': 'L. Frank',\n",
       "     'last_name': 'Baum',\n",
       "     'dob': '1856',\n",
       "     'dod': '1919'}],\n",
       "   'genre': ['Action & Adventure'],\n",
       "   'Dramatic Readings': False,\n",
       "   'meta_genre': 'Literature'},\n",
       "  'snr': 11.4471,\n",
       "  'voice_activity': [[1.52, 11.2],\n",
       "   [11.84, 14.08],\n",
       "   [15.12, 35.76],\n",
       "   [36.32, 55.6],\n",
       "   [56.24, 70.48],\n",
       "   [71.28, 79.52],\n",
       "   [80.08, 89.76],\n",
       "   [90.24, 97.52],\n",
       "   [98.0, 101.28],\n",
       "   [102.8, 124.88],\n",
       "   [125.36, 133.12],\n",
       "   [133.68, 154.16],\n",
       "   [154.64, 177.2],\n",
       "   [178.0, 196.96],\n",
       "   [197.68, 211.44],\n",
       "   [212.32, 216.32],\n",
       "   [216.96, 243.52],\n",
       "   [244.0, 250.72],\n",
       "   [251.52, 268.32],\n",
       "   [268.96, 308.56],\n",
       "   [309.04, 315.28],\n",
       "   [316.0, 317.36],\n",
       "   [317.92, 325.44],\n",
       "   [326.24, 343.6],\n",
       "   [344.08, 350.32],\n",
       "   [350.88, 356.64],\n",
       "   [357.2, 363.2],\n",
       "   [363.76, 365.2],\n",
       "   [365.2, 373.2],\n",
       "   [373.84, 392.0],\n",
       "   [392.56, 401.04],\n",
       "   [401.6, 456.96],\n",
       "   [457.68, 501.92],\n",
       "   [502.4, 531.04],\n",
       "   [531.6, 554.48],\n",
       "   [554.96, 568.32],\n",
       "   [568.96, 585.84],\n",
       "   [587.04, 588.48],\n",
       "   [597.12, 597.92]]},\n",
       " 'vad.npy': array([[  1.764,   6.49 ],\n",
       "        [  6.773,  11.18 ],\n",
       "        [ 11.98 ,  14.03 ],\n",
       "        [ 15.31 ,  36.3  ],\n",
       "        [ 36.3  ,  56.06 ],\n",
       "        [ 56.4  ,  70.6  ],\n",
       "        [ 71.4  , 101.2  ],\n",
       "        [102.75 , 103.56 ],\n",
       "        [103.7  , 121.75 ],\n",
       "        [122.06 , 125.   ],\n",
       "        [125.44 , 133.4  ],\n",
       "        [133.8  , 154.6  ],\n",
       "        [154.6  , 177.6  ],\n",
       "        [178.1  , 197.2  ],\n",
       "        [197.9  , 212.1  ],\n",
       "        [212.5  , 222.5  ],\n",
       "        [222.8  , 243.6  ],\n",
       "        [244.2  , 246.5  ],\n",
       "        [246.8  , 251.1  ],\n",
       "        [251.5  , 256.2  ],\n",
       "        [256.5  , 257.8  ],\n",
       "        [258.2  , 259.8  ],\n",
       "        [259.8  , 268.5  ],\n",
       "        [269.2  , 289.8  ],\n",
       "        [289.8  , 315.8  ],\n",
       "        [316.   , 317.2  ],\n",
       "        [318.   , 319.   ],\n",
       "        [319.8  , 344.   ],\n",
       "        [344.2  , 350.2  ],\n",
       "        [351.   , 352.5  ],\n",
       "        [353.   , 356.8  ],\n",
       "        [357.5  , 373.5  ],\n",
       "        [374.   , 388.   ],\n",
       "        [388.2  , 397.2  ],\n",
       "        [397.5  , 401.5  ],\n",
       "        [401.8  , 423.5  ],\n",
       "        [423.5  , 448.   ],\n",
       "        [448.   , 457.2  ],\n",
       "        [457.8  , 460.8  ],\n",
       "        [461.   , 477.8  ],\n",
       "        [478.5  , 502.2  ],\n",
       "        [502.2  , 527.5  ],\n",
       "        [527.5  , 550.5  ],\n",
       "        [550.5  , 576.5  ],\n",
       "        [577.   , 586.   ],\n",
       "        [587.5  , 588.5  ]], dtype=float16)}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for s in ds: break\n",
    "s # notice the 'vad.npy' values that was missing from the FLAC dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "152144f8",
   "metadata": {},
   "source": [
    "## Split the audio into chunks\n",
    "\n",
    "After we merge the datasets and chunk the segments we can split each audio file into individual samples and pad them to 30s."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b58157a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| exporti\n",
    "def split_to_chunks(stream, pad_to_seconds=30, random_shift=False):\n",
    "    for s in stream:\n",
    "        audio, sr = s.get('flac', s.get('wav', (None, None)))\n",
    "        if audio is None:\n",
    "            print(f\"warning: '{s['__key__']}' does not contain an audio file\")\n",
    "            continue\n",
    "        imax = len(s['vad.npy']) - 1\n",
    "        for i,(ts,te) in enumerate(s['vad.npy']):\n",
    "            samples = audio[0,int(ts*sr):int(te*sr)]\n",
    "            if pad_to_seconds is not None:\n",
    "                padding = pad_to_seconds*sr-samples.shape[-1]\n",
    "                lpad = random.randint(0, padding) if random_shift else 0\n",
    "                samples = F.pad(samples, (lpad, padding-lpad))\n",
    "            yield {\"__key__\": s['__key__'] + f\"_{i:03d}\",\n",
    "                   \"__url__\": s['__url__'],\n",
    "                   \"i\": i, \"imax\": imax,\n",
    "                   \"tstart\": ts, \"tend\": te, \"total_seconds\": audio.shape[-1]/sr,\n",
    "                   \"lpad\": lpad, \"rpad\": padding-lpad,\n",
    "                   \"lpad_s\": lpad/sr, \"rpad_s\": (padding-lpad)/sr,\n",
    "                   \"samples\": samples, \"sample_rate\": sr}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0436d350",
   "metadata": {},
   "outputs": [],
   "source": [
    "split_ds = wds_compose(ds,\n",
    "   wds.map_dict(**{\"vad.npy\":chunk_merger}),\n",
    "   split_to_chunks,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a677578f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'__key__': 'small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_000',\n",
       " '__url__': 'librilight-small-vad-000000.tar.gz',\n",
       " 'i': 0,\n",
       " 'imax': 27,\n",
       " 'tstart': 1.764,\n",
       " 'tend': 14.03,\n",
       " 'total_seconds': 597.9425,\n",
       " 'samples': tensor([0., 0., 0.,  ..., 0., 0., 0.])}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for s in split_ds: break\n",
    "s"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96b8c5d1",
   "metadata": {},
   "source": [
    "## Transcribe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6d46dc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "whmodel = whisper.load_model('base.en')\n",
    "decoding_options = whisper.DecodingOptions(language='en')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc1aad2c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<style>\n",
       "    /* Turns off some styling */\n",
       "    progress {\n",
       "        /* gets rid of default border in Firefox and Opera. */\n",
       "        border: none;\n",
       "        /* Needs to be in here for Safari polyfill so background images work as expected. */\n",
       "        background-size: auto;\n",
       "    }\n",
       "    progress:not([value]), progress:not([value])::-webkit-progress-bar {\n",
       "        background: repeating-linear-gradient(45deg, #7e7e7e, #7e7e7e 10px, #5c5c5c 10px, #5c5c5c 20px);\n",
       "    }\n",
       "    .progress-bar-interrupted, .progress-bar-interrupted::-webkit-progress-bar {\n",
       "        background: #F44336;\n",
       "    }\n",
       "</style>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      <progress value='256' class='' max='256' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      100.00% [256/256 00:59&lt;00:00]\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "output = flac_url.rsplit(\"/\", 1)[1].replace('flac', 'txt') + \".gz\"\n",
    "with wds.TarWriter(output) as sink:\n",
    "    for s in progress_bar(split_ds, total=256):\n",
    "        mel = whisper.log_mel_spectrogram(s['samples'].unsqueeze(0).cuda())\n",
    "        embs = whmodel.encoder(mel)\n",
    "        decs = whmodel.decode(embs, decoding_options)\n",
    "\n",
    "        sink.write({\n",
    "            \"__key__\": s['__key__'],\n",
    "            \"txt\": decs[0].text,\n",
    "        })"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47dfd9fc",
   "metadata": {},
   "source": [
    "## Transcribe in batches\n",
    "\n",
    "We have one more thing to add – batch processing makes the transcription quite a bit faster (bs=16 brings a 4.5x speedup)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "352ae96b",
   "metadata": {},
   "outputs": [],
   "source": [
    "batched_ds = wds_compose(split_ds,\n",
    "    wds.to_tuple('__key__', 'samples'),\n",
    "    wds.batched(16),\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8efbde3e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<style>\n",
       "    /* Turns off some styling */\n",
       "    progress {\n",
       "        /* gets rid of default border in Firefox and Opera. */\n",
       "        border: none;\n",
       "        /* Needs to be in here for Safari polyfill so background images work as expected. */\n",
       "        background-size: auto;\n",
       "    }\n",
       "    progress:not([value]), progress:not([value])::-webkit-progress-bar {\n",
       "        background: repeating-linear-gradient(45deg, #7e7e7e, #7e7e7e 10px, #5c5c5c 10px, #5c5c5c 20px);\n",
       "    }\n",
       "    .progress-bar-interrupted, .progress-bar-interrupted::-webkit-progress-bar {\n",
       "        background: #F44336;\n",
       "    }\n",
       "</style>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      <progress value='16' class='' max='16' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      100.00% [16/16 00:11&lt;00:00]\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "output = flac_url.rsplit(\"/\", 1)[1].replace('flac', 'txt') + \".gz\"\n",
    "with wds.TarWriter(output) as sink:\n",
    "    for keys, samples in progress_bar(batched_ds, total=256//16):\n",
    "        mel = whisper.log_mel_spectrogram(samples).cuda()\n",
    "        embs = whmodel.encoder(mel)\n",
    "        decs = whmodel.decode(embs, decoding_options)\n",
    "        for key, dec in zip(keys, decs):\n",
    "            sink.write({\n",
    "                \"__key__\": key,\n",
    "                \"txt\": dec.text,\n",
    "            })"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72959d99",
   "metadata": {},
   "source": [
    "## Verify the transcripts and the chunks work together"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e8ae1b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "txt_ds = wds_compose(split_ds,\n",
    "    merge_in(wds.WebDataset(flac_url.rsplit(\"/\", 1)[1].replace('flac', 'txt') + \".gz\").decode())\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9bc463a8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'__key__': 'small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_000',\n",
       " '__url__': 'librilight-small-txt-000000.tar.gz',\n",
       " 'i': 0,\n",
       " 'imax': 27,\n",
       " 'tstart': 1.764,\n",
       " 'tend': 14.03,\n",
       " 'total_seconds': 597.9425,\n",
       " 'samples': tensor([0., 0., 0.,  ..., 0., 0., 0.]),\n",
       " 'txt': 'This is a LibraVox recording. Holy bivox recordings are in the public domain. For more information or to volunteer, please visit libravox.org.'}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for x in txt_ds: break\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54968ab1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<style>\n",
       "    /* Turns off some styling */\n",
       "    progress {\n",
       "        /* gets rid of default border in Firefox and Opera. */\n",
       "        border: none;\n",
       "        /* Needs to be in here for Safari polyfill so background images work as expected. */\n",
       "        background-size: auto;\n",
       "    }\n",
       "    progress:not([value]), progress:not([value])::-webkit-progress-bar {\n",
       "        background: repeating-linear-gradient(45deg, #7e7e7e, #7e7e7e 10px, #5c5c5c 10px, #5c5c5c 20px);\n",
       "    }\n",
       "    .progress-bar-interrupted, .progress-bar-interrupted::-webkit-progress-bar {\n",
       "        background: #F44336;\n",
       "    }\n",
       "</style>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      <progress value='10' class='' max='10' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      100.00% [10/10 00:00&lt;00:00]\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_000 chunk 0 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-0.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "This is a LibraVox recording. Holy bivox recordings are in the public domain. For more information or to volunteer, please visit libravox.org."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_001 chunk 1 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-1.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "The oceans are being in broad. I believe two-thirds of the Earth's surface is covered with water. What people inhabit this water has always been a subject of curiosity to the inhabitants of the land. Strange creatures come from the seas at times, and perhaps in the ocean depths are many more strange than mortal eye has ever gazed upon."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_002 chunk 2 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-2.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "This story is fanciful. In it the sea people talk and act much as we do, and the mermaids especially are not unlike the fairies with whom we have learned to be familiar. Yet they are real sea people for all that, and with the exception of Zog the magician, they are all supposed to exist in the ocean steps."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_003 chunk 3 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-3.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "I am told that some very learned people deny that mermaids or sea serpents have ever inhabited the oceans. But it would be very difficult for them to prove such an assertion, unless they had lived under the water as trot and caten-billed did in this story."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_004 chunk 4 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-4.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "I hope my readers who have so long followed Dorothy's adventures in the Land of Oz will be interested in Trot's equally strange experiences. The ocean has always appealed to me as a veritable wonderland, and this story has been suggested to me many times by my young correspondence in their letters. Indeed, a good many children have implored me to write something about the mermaids, and I have willingly granted the request."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_005 chunk 5 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-5.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "CHAPTER I. TROT AND CAPTAIN BILL. Nobody said Captain Bill solemnly, ever saw a mermaid and lived to tell the tale. Why not, asked TROT, looking earnestly up into the old sailor's face? They were seated on a bench built around a giant acacia tree that grew just at the edge of the bluff. Below them rolled the blue waves of the great Pacific."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_006 chunk 6 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-6.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "A little way behind them was the house, a neat framed cottage painted white and surrounded by huge eucalyptus and pepper trees."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_007 chunk 7 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-7.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "Still farther behind that, a quarter of a mile distant but built upon a bend of the coast was the village overlooking a pretty bay. Catonville and Trot came often to this tree to sit and watch the ocean below them. The sailor man had one meat leg and one hickory leg, and he often said the wooden one was the best of the two."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_008 chunk 8 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-8.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "Once Catenville had commanded and owned the anemone, a trading schooner that plied along the coast, and in those days Charlie Griffin's, who was trot's father, had been the captain's mate. But ever since Catenville's accident when he lost his leg, Charlie Griffiths had been the captain of the little schooner, while his old master lived peacefully ashore with the Griffiths family."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "#### small/100/sea_fairies_0812_librivox_64kb_mp3/01_baum_sea_fairies_64kb_009 chunk 9 of 27"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" >\n",
       "                    <source src=\"test-9.ogg\" type=\"audio/ogg\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "This was about the time Trot was born, and the old sailor became very fond of the baby girl. Her real name was Mary, but when she grew big enough to walk, she took so many busy little steps every day that both her mother and Captain Bill nicknamed her Trot, and so she was thereafter mostly called."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for x in progress_bar(txt_ds, total=10):\n",
    "    IPython.display.display(IPython.display.Markdown(f\"#### {x['__key__']} chunk {x['i']} of {x['imax']}\"))\n",
    "    fname = f\"test-{x['i']}.ogg\"\n",
    "    torchaudio.save(fname, x['samples'][None,:int((x['tend']-x['tstart'])*16000)], 16000)\n",
    "    IPython.display.display(IPython.display.Audio(url=fname, rate=16000))\n",
    "    IPython.display.display(IPython.display.Markdown(x['txt']))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b52e3d6",
   "metadata": {},
   "source": [
    "## Batch processing\n",
    "\n",
    "Let's put everything above together."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b1f5754",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| exporti\n",
    "def flac_to_txt_name(input, model_size):\n",
    "    return input.rsplit(\"/\", 1)[1].replace('flac', f'{model_size}-txt') + \".gz\"\n",
    "\n",
    "@call_parse\n",
    "def process_shard(\n",
    "    input:str,          # input shard URL/path\n",
    "    output:str=None,    # output shard URL/path\n",
    "    bs:int=None,        # batch size (16 uses around 11GB of VRAM)\n",
    "    n_samples:int=None, # limit the number of samples (useful for quick benchmarking)\n",
    "    whisper_model:str=\"base.en\" # Whisper model size\n",
    "):\n",
    "    if output is None: output = flac_to_txt_name(input, whisper_model)\n",
    "    if bs is None: bs = 16\n",
    "    if n_samples is None: n_samples = 'noinfer'\n",
    "    else: n_samples = n_samples // bs\n",
    "\n",
    "    ds = wds_compose(vad.load_dataset(input),\n",
    "        merge_in(wds.WebDataset(vad.flac_to_vad_name(input)).decode()),\n",
    "        wds.map_dict(**{\"vad.npy\":chunk_merger}),\n",
    "        split_to_chunks,\n",
    "        wds.to_tuple('__key__', 'samples'),\n",
    "        wds.batched(bs),\n",
    "    )\n",
    "    dl = DataLoader(ds, num_workers=2, batch_size=None)\n",
    "    \n",
    "    whmodel = whisper.load_model(whisper_model)\n",
    "    decoding_options = whisper.DecodingOptions(language='en')\n",
    "    \n",
    "    tmp = output+\".tmp\"\n",
    "    with wds.TarWriter(tmp) as sink:\n",
    "        for keys, samples in progress_bar(dl, total=n_samples):\n",
    "            with torch.no_grad():\n",
    "                embs = whmodel.encoder(whisper.log_mel_spectrogram(samples).cuda())\n",
    "                decs = whmodel.decode(embs, decoding_options)\n",
    "            for key, dec in zip(keys, decs):\n",
    "                sink.write({\n",
    "                    \"__key__\": key,\n",
    "                    \"txt\": dec.text,\n",
    "                })\n",
    "    os.rename(tmp, output)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
